package com.biye.api.tool.security.filter;

import com.alibaba.fastjson.JSONObject;
import com.base.common.edu.util.Dateutil;
import com.base.common.edu.util.RequestUtil;
import com.base.common.httpStatusCode.enumDefine.StatusCode;
import com.base.common.security.util.JWTUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author Maple.Chen
 * @Description Security入关的第一道闸口，就是在这给Authentication赋值，这样后面使用Authentication才会畅通无阻。
 * @Date 15:13 2021/3/7
 **/

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final static Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Resource
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 就如以前学Servlet的过滤器一样，验证失败即返回json
        boolean accessFlag = false;
        // 获取Token
        String token_Identifier = request.getHeader("Identifier");
        if ( StringUtils.isNotBlank(token_Identifier) ) {
            // 取出token中的属性
            String userName = JWTUtil.getClaimValue(token_Identifier, "userName");
            String userId = JWTUtil.getClaimValue(token_Identifier, "userId");
            String userType = JWTUtil.getClaimValue(token_Identifier, "userType");
            logger.info("[userName：" + userName + "][userId：" + userId + "][userType：" + userType + "][Time：" + Dateutil.getCurrentDateText() + "][IP：" + RequestUtil.getIpAddress(request) + "]");

            // 判断token中是否有“有效属性”
            if ( userId != null && userType != null && SecurityContextHolder.getContext().getAuthentication() == null){
                // 根据用户身份查用户权限等
                StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append(userId);
                stringBuffer.append("@");
                stringBuffer.append(userType);
                UserDetails userDetails = userDetailsService.loadUserByUsername(stringBuffer.toString());
                // 判断数据库有这个用户，然后再验证token的有效性包括是否过期
                if( userDetails.getUsername() != null && JWTUtil.verify(token_Identifier) ){
                    UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(/*userDetails.getUsername()*/stringBuffer.toString(),
                            /*userDetails.getPassword()*/null, userDetails.getAuthorities());
                    auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    //设置用户登录状态，认证通过后写入Context，这样请求就不会被Security拦截了
                    logger.info("authenticated userId&Type {}, setting security context", stringBuffer.toString());
                    SecurityContextHolder.getContext().setAuthentication(auth);
                    accessFlag = true;
                }
            }else if (SecurityContextHolder.getContext().getAuthentication() != null) {
                accessFlag = true;
            }
        }

        // 判断用户token验证是否正确，不正确则拦截
        if ( accessFlag ){
            // 验证通过，后边由Security对Authentication进行验证。
            filterChain.doFilter(request, response);
        }else{
            /*
                这里是对401(AuthenticationException)错误的封装
             */
            //设置头信息
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-type","application:json;charset=utf-8");
            //配置返回参数
            JSONObject jo=new JSONObject();
            jo.put("statusCode", StatusCode.NoLogin._statusCode);
            jo.put("msg", "访问未授权,无法访问");
            //返回提示参数
            response.getWriter().write(jo.toJSONString());
        }
    }

}